首页/技术教程/不装 Prettier 扩展,也能在 VS Code 里用 oxfmt + Prettier 把项目格式化“管起来”

很多团队的现实情况是:

  • 不想强依赖 VS Code 的 Prettier 扩展(每个人装的版本不一、设置不一、偶尔还会和别的格式化器打架)。
  • 想把“格式化”收敛到项目层:命令行统一、CI 统一、提交前统一。
  • 同时又想享受保存即格式化的手感。

这篇文章分享一个我在项目里常用的通用方案:

  • JS/TS/JSX/TSX:交给 oxfmt(快、稳定、偏工程化)。
  • Markdown/JSON/YAML/CSS/HTML 等:交给 prettier CLI(生态成熟、覆盖面广)。
  • VS Code 端:不装 Prettier 扩展,仍然做到“能一键格式化、能提交前自动格式化、需要的话也能自动保存触发格式化”。

重要前提:oxfmt 目前没有 VS Code 官方/主流 formatter 扩展(至少在多数团队的默认配置里是这样),所以本文的所有 oxfmt 都按“命令行工具(CLI)”来使用:

  • 你可以用 RunOnSave 在保存时触发它
  • 也可以用 VS Code Task 一键触发
  • 最稳的是提交前钩子(lint-staged)兜底

先把原则定清楚:格式化应该归谁管?

我推荐的优先级是:

  1. 项目工具链(pnpm scripts + CLI)是最终裁判:任何人、任何 IDE,跑同一条命令,输出一致。
  2. VS Code 只是触发器:
    • 能自动触发 CLI 的(例如用 RunOnSave)就做到“保存即格式化”。
    • 其他情况(例如不想在保存时跑命令),就交给“任务 / 提交钩子 / CI”。
  3. 永远避免“双格式化”:同一类文件只交给一个 formatter。

这样做最大的好处:你不靠某个编辑器扩展也能保证一致性


分工:oxfmt 管 JS/TS,prettier 管其余

为什么 oxfmt 适合 JS/TS?

  • 快:保存时格式化的体感更好。
  • 和现代前端工程化思路更贴合:把“统一风格”变成工具链的一部分。

为什么 prettier 仍然要保留?

因为它的覆盖面太强了:Markdown、YAML、JSON、HTML、CSS……很多项目里“真正乱”往往就乱在这些文件上。


方案总览(你照这个搭就能跑)

你需要准备 5 类东西:

  1. prettier 配置(项目根:prettier.config.* / .prettierrc* + .prettierignore
  2. oxfmt 配置(项目根:.oxfmt* 或 oxfmt/oxc 相关的配置文件,按你的工具版本来)
  3. pnpm scripts:一键格式化(全量/增量)
  4. VS Code .vscode/settings.json:让 JS/TS 保存时走 oxfmt
  5. VS Code .vscode/tasks.json + 提交钩子:不靠 Prettier 扩展也能格式化 Markdown/JSON/YAML…

下面我按“你能直接复制走”的方式给出参考配置。


第 1 步:prettier 只管它该管的文件(避免和 oxfmt 打架)

1).prettierignore:把 JS/TS 排除掉

核心思路:让 prettier 不碰 JS/TS(否则你会遇到“保存格式化一次、提交又格式化一次、diff 永远有变化”的地狱)。

例如:

# 让 oxfmt 负责 JS/TS/JSX/TSX
**/*.{js,jsx,ts,tsx}

# 构建产物
dist
.vitepress/dist
node_modules

# 其他你不想格式化的目录
**/CHANGELOG.md

2)prettier.config.*:把“可控的格式”写死

你可以用 prettier.config.mjs.prettierrc,关键是可被 CLI 读到

配置项别搞花活,推荐只锁几个关键的:

// prettier.config.mjs(示例)
export default {
    printWidth: 100,
    tabWidth: 4,
    singleQuote: true,
    semi: false,
    trailingComma: "all",
    proseWrap: "preserve"
};

注意:你项目里 Markdown 的排版习惯(比如列表缩进、中文标点与英文间距)会直接影响读感,建议把 printWidthtabWidth 固化,避免不同人不同风格。


第 2 步:pnpm scripts 统一入口(“不靠扩展”的关键)

你最终要的体验是:

  • 格式化全项目pnpm format
  • 只格式化某个文件pnpm format:file -- <file>
  • 提交前自动格式化变更文件:git hooks + lint-staged

一个常用脚本结构是:

{
    "scripts": {
        "format": "pnpm -s format:oxfmt && pnpm -s format:prettier",
        "format:oxfmt": "pnpm -s exec oxfmt --write .",
        "format:prettier": "pnpm -s exec prettier --write . --cache --ignore-unknown",
        "format:file": "node scripts/format-file.mjs"
    }
}

补充一句:这里用 && 是为了在 Windows/macOS/Linux 都能稳定串行执行;不要用 ;,否则在 Windows 的默认 shell 下可能会翻车。

说明:

  • format 只是聚合命令,保证任何人执行顺序一致。
  • format:oxfmt / format:prettier 分开写,方便在任务/钩子里只跑需要的那一段。

如果你不想写 scripts/format-file.mjs,也可以只做“全量 + 提交前增量”,足够覆盖 90% 场景。


第 3 步:VS Code 不装 Prettier 扩展,也能“保存即格式化”(用 CLI 触发)

这里先把“真相”说清楚:

  • VS Code 的 editor.formatOnSave 依赖“某个扩展提供 formatter”。
  • 既然 oxfmt 没有 VS Code formatter 扩展,那就没法靠 formatOnSave 直接调用 oxfmt。

所以我们用两条路来实现“保存即格式化”的体验(本质都是保存时触发 CLI):

  1. RunOnSave 在保存时调用 oxfmt(适用于 JS/TS)。
  2. RunOnSave 在保存时调用 prettier(适用于 Markdown/JSON/YAML/HTML/CSS)。

这也是本文的核心:用 CLI 做统一入口,用一个“保存触发器”把体验补齐


第 4 步:不装 Prettier 扩展时,Markdown/JSON/YAML 怎么格式化?

这里要分清楚一件事:

  • VS Code 的“保存即格式化”依赖 formatter provider
  • 你不装 Prettier 扩展时,很多文件类型(尤其 Markdown)就没有你想要的 provider。

所以我们用“项目工具链”的方式补上:

方案 A(我最推荐):提交前自动格式化(lint-staged)

这是“最不折腾、最一致”的方案:你可以不在每次保存时格式化 Markdown,但你提交时一定会被格式化

典型配置(你可以用 simple-git-hookshusky,二选一):

{
    "simple-git-hooks": {
        "pre-commit": "pnpm lint-staged"
    },
    "lint-staged": {
        "*.{js,jsx,ts,tsx}": "pnpm -s exec oxfmt --write",
        "*.{css,scss,less,html,json,md,yaml,yml}": "pnpm -s exec prettier --write --cache --ignore-unknown"
    }
}

关键点:

  • 文件后缀分流:JS/TS 走 oxfmt,其余走 prettier。
  • 这就实现了:不装 Prettier 扩展也能统一风格

方案 B:VS Code 一键任务(格式化当前文件 / 全项目)

你可以在 .vscode/tasks.json 里加两个任务:

  • Format (workspace):跑 pnpm format
  • Format (current file):跑 prettier --write ${file} 或根据后缀选择 oxfmt/prettier

这样你就能在 VS Code 里:

  • 需要时按一下快捷键(或命令面板选任务),把 Markdown/JSON/YAML 也一把梭。

这个方案的好处是:零依赖 Prettier 扩展。坏处是:它不是自动的,需要你主动触发一次。

方案 C(想要“保存即格式化”但不装 Prettier 扩展):用 RunOnSave 在保存时触发 CLI

如果你真的特别在意 Markdown 的“保存即格式化”,但仍不想装 Prettier 扩展,那只能走“保存时跑 CLI”的路线。

实现方式我推荐直接用这个扩展:

  • Run on Save(扩展 ID:emeraldwalk.RunOnSave
  • Marketplace:https://marketplace.visualstudio.com/items?itemName=emeraldwalk.RunOnSave

我自己用 fetch_webpage 抓过它的说明页,确认它支持:

  • match / notMatch 做正则匹配(注意:匹配的是绝对路径
  • 保存时执行命令(支持串行/并行 isAsync
  • 支持 ${file} / ${workspaceFolder} / ${fileBasename} 等占位符

下面给一个“够用且不容易炸”的配置范式(放到工作区的 .vscode/settings.json 或用户设置里都行):

{
    "emeraldwalk.runonsave": {
        "showElapsed": true,
        "commands": [
            {
                // 让 oxfmt 负责的文件:js/ts/jsx/tsx
                "match": "\\.(js|jsx|ts|tsx)$",
                "notMatch": "(\\\\node_modules\\\\|\\\\dist\\\\|\\\\\\.vitepress\\\\dist\\\\|\\\\\\.vscode\\\\)",
                "cmd": "pnpm -s exec oxfmt --write \"${file}\""
            },
            {
                // 让 prettier 负责的文件:md/json/yaml/html/css...
                "match": "\\.(md|json|ya?ml|yml|html|css|scss|less)$",
                // 不要动依赖/产物/编辑器配置目录
                // 注意:Windows 下路径分隔符是 \\,而 JSON 里反斜杠要双重转义
                "notMatch": "(\\\\node_modules\\\\|\\\\dist\\\\|\\\\\\.vitepress\\\\dist\\\\|\\\\\\.vscode\\\\)",
                "cmd": "pnpm -s exec prettier --write --cache --ignore-unknown \"${file}\""
            }
        ]
    }
}

这样你就能做到:

  • JS/TS:保存时由 RunOnSave 触发 oxfmt(CLI)
  • Markdown/JSON/YAML/HTML/CSS:保存时由 RunOnSave 触发 prettier CLI

官方还特别提醒了一点(很关键):RunOnSave 只会在“保存已有文件”时触发,新建文件第一次保存、以及“另存为”不会触发——这不是 bug,是它的设计。

我个人仍然更推荐 A + B;如果你真的强依赖“文档保存即格式化”,再加上 C

  • 写代码(JS/TS)靠 oxfmt 保存即格式化
  • 文档类靠任务一键
  • 提交前靠 lint-staged 保底

体验足够好,而且不会引入新的不稳定因素。


常见坑(提前踩掉)

1)同一文件被两套格式化器处理

症状:你总觉得“格式化完又乱了”“diff 永远有”。

解决:

  • 让 prettier 忽略 *.{js,ts,jsx,tsx}(或让 lint-staged 分流)。
  • VS Code 的默认 formatter 只对 JS/TS 指向 oxfmt。

2)Windows 路径与引号

任务里用 ${file} 时,如果路径包含空格,命令行引号一定要处理好。

建议做法:

  • 优先跑 pnpm format(全量)或 lint-staged(增量),避免自己在 tasks 里拼复杂引号。

3)团队里有人用 WebStorm / Cursor / 其他 IDE

这套方案天然兼容:

  • 他们只要能跑 pnpm format / 提交代码,就会自动统一风格。
  • VS Code 里的“格式化体验”只是锦上添花,不是硬依赖。

最后总结(一句话)

把格式化收敛到项目工具链:JS/TS 用 oxfmt(编辑器保存即格式化),其余用 prettier CLI(任务 + 提交前钩子兜底),需要“保存即格式化”时就用 emeraldwalk.RunOnSave 触发 prettier CLI ——就能做到“不装 Prettier 扩展也一致”。